package com.eastmoney.emis.edda.task.eddi.processor;

import com.eastmoney.emis.edda.config.HsbcEddaConfig;
import com.eastmoney.emis.edda.domain.entity.EddiInstruction;
import com.eastmoney.emis.edda.domain.event.EddiInstructionEvent;
import com.eastmoney.emis.edda.domain.event.EventPublisher;
import com.eastmoney.emis.edda.domain.repository.HsbcEddiRepository;
import com.eastmoney.emis.edda.domain.valueobject.EddiOperation;
import com.eastmoney.emis.edda.domain.valueobject.EddiStatus;
import com.eastmoney.emis.edda.domain.valueobject.PaymentStatus;
import com.eastmoney.emis.edda.infrastructure.edda.service.HsbcApiErrorResolver;
import com.eastmoney.emis.edda.infrastructure.hsbc.HsbcEddaApi;
import com.eastmoney.emis.edda.infrastructure.hsbc.dto.*;
import com.eastmoney.emis.edda.task.eddi.EddiTask;
import com.eastmoney.emis.logger.LoggerFactory;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * @description: Description
 * @projectName: EDDA-HSBC
 * @package: com.eastmoney.emis.edda.task.eddi
 * @author: 汪程程
 * @date: 2022/10/13 2:27 PM
 * @version: 1.0
 */
@Order(1)
@Component
public class EddiBankProcessProcessor implements EddiProcessor {

    private static Logger log = LoggerFactory.getLogger(EddiBankProcessProcessor.class);
    @Autowired
    private HsbcEddaConfig config;
    @Autowired
    private HsbcEddaApi eddaApi;
    @Autowired
    private HsbcEddiRepository eddiRepository;
    @Autowired
    private HsbcApiErrorResolver errorResolver;
    @Autowired
    private EventPublisher eventPublisher;

    @Override
    public boolean canProcess(String status) {
        return EddiStatus.BANK_PROCESS.value().equals(status);
    }

    @Override
    public void execute(EddiTask task) {
        EddiInstruction eddiInstruction = task.getEddiInstruction();
        LocalDateTime now = LocalDateTime.now();
        // 时间间隔小于15s
        if (now.minusSeconds(15).isBefore(eddiInstruction.getUpdatedTime())) {
            log.info("task = [{},{}] 小于 15s 忽略", task.getId(), task.getName());
            return;
        }
        InstructionStatusReq req = new InstructionStatusReq();
        req.MerchantInstructionIdentification = eddiInstruction.getApplySerialNo();
        req.CreditorAccount = new AccountDto(config.getCreditorAccount());
        InstructionStatusResp resp = eddaApi.debitInstructionStatus(req);
        resp.onSuccess(r -> {
            eddiInstruction.setTransactionId(resp.TransactionIdentification);
            eddiInstruction.setStatus(PaymentStatus.parse(resp.PaymentInformationStatus).transferToEddiStatus());
            eddiRepository.saveDebitInstruction(eddiInstruction);
            publishEvent(task, resp, EddiOperation.BANK_PROCESS);
        }).onFail(r -> {
            eddiInstruction.setStatus(EddiStatus.BANK_PROCESS_FAIL);
            publishEvent(task, resp, EddiOperation.BANK_PROCESS);
            if (!isNeedRetry(resp.errorCodes())) {
                eddiRepository.saveDebitInstruction(eddiInstruction);
                return;
            }
            // retry
            executeEddiRetry(task, eddiInstruction);
        }).onNetworkError(r -> errorResolver.resolveAndTrow(resp));

    }

    private boolean isNeedRetry(String errorCodes) {
        // MFP00001:Transaction not found
        return errorCodes.contains("MFP00001");
    }

    private void executeEddiRetry(EddiTask task, EddiInstruction eddiInstruction) {
        eddiInstruction.setStatus(EddiStatus.BANK_PROCESS);
        InstructionReq req = new InstructionReq();
        req.MerchantInstructionIdentification = eddiInstruction.getApplySerialNo();
        req.MandateIdentification = eddiInstruction.getMandateId();
        req.CreditorAccount = new AccountDto(config.getCreditorAccount());
        req.InstructedAmount = eddiInstruction.getOccurBalance().toString();
        req.CreditorReference = task.getCreditorReference();
        req.SmsLanguageCode = task.getLanguage();
        InstructionResp resp = eddaApi.debitInstruction(req);
        resp.onSuccess(r -> {
            eddiInstruction.setTransactionId(resp.TransactionIdentification);
            eddiInstruction.setStatus(PaymentStatus.parse(resp.PaymentInformationStatus).transferToEddiStatus());
        }).onFail(r -> {
            eddiInstruction.setStatus(EddiStatus.BANK_PROCESS_FAIL);
        });
        eddiRepository.saveDebitInstruction(eddiInstruction);
        publishEvent(task, resp, EddiOperation.BANK_PROCESS_RETRY);
        errorResolver.resolveAndTrow(resp);
    }

    private void publishEvent(EddiTask task, HsbcEddaBaseResp resp, EddiOperation operation) {
        EddiInstructionEvent event = new EddiInstructionEvent(task.getEddiInstruction());
        event.setProcessUid(task.getProcessUid());
        event.setProcessUserName(task.getProcessUserName());
        event.setResult(resp.result());
        event.setApplyReq(resp.context().getReqPayload());
        event.setApplyRes(resp.context().getRespPayload());
        event.setOperation(operation.getValue());
        event.setErrorReason(resp.rejectReason());
        eventPublisher.publish(event);
    }

}
